﻿/*
 * Tencent is pleased to support the open source community by making xLua available.
 * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
 * http://opensource.org/licenses/MIT
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/

using UnityEngine;
using XLua;
using System;
using DG.Tweening;

namespace Al_FrameWork
{
    [LuaCallCSharp]
    public class LuaMyBehaviour : MonoBehaviour
    {
        public string luaScriptPath;
        public TextAsset luaScript;
        public VariableArray variables;
        internal static LuaEnv luaEnv; //all lua behaviour shared one luaenv only!
        internal static float lastGCTime = 0;
        internal const float GCInterval = 1; //1 second 

        private object luaObj;
        private Action luaStart;
        private Action luaUpdate;
        private Action luaOnDestroy;
        private Action luaOnEnable;
        private Action luaOnDisable;
        private Action luaOnGUI;
        private Action luaLateUpdate;
        private Action<Collider> luaOnTriggerEnter;

        private static string registFunc = "\nlocal curObj\n" +
                                           "function Awake()\n" +
                                           "\tcurObj = CurObj.new(CurObj.__cname, self)\n" +
                                           "\tcurObj:Awake()\n" +
                                           "end\n" +
                                           "function Start()\n" +
                                           "\tcurObj:Start()\n" +
                                           "end\n" +
                                           "function Update()\n" +
                                           "\tcurObj:Update()\n" +
                                           "end\n" +
                                           "function LateUpdate()\n" +
                                           "\tcurObj:LateUpdate()\n" +
                                           "end\n" +
                                           "function OnDestroy()\n" +
                                           "\tcurObj:OnDestroy()\n" +
                                           "end\n" +
                                           "function OnEnable()\n" +
                                           "\tcurObj:OnEnable()\n" +
                                           "end\n" +
                                           "function OnTriggerEnter(other)\n" +
                                           "\tcurObj:OnTriggerEnter(other)\n" +
                                           "end\n" +
                                           "function OnGUI()\n" +
                                           "\tcurObj:OnGUI()\n" +
                                           "end\n" +
                                           "function OnDisable()\n" +
                                           "\tcurObj:OnDisable()\n" +
                                           "end\n";

        private LuaTable scriptScopeTable;

        void Awake()
        {
            luaEnv = XLuaManager.Instance.GetLuaEnv();
#if UNITY_EDITOR
            if (luaEnv == null)
            {
                XLuaManager.Instance.Init();
                luaEnv = XLuaManager.Instance.GetLuaEnv();
            }
#endif
            // 为每个脚本设置一个独立的脚本域，可一定程度上防止脚本间全局变量、函数冲突
            scriptScopeTable = luaEnv.NewTable();
            // 设置其元表的 __index, 使其能够访问全局变量
            using (LuaTable meta = luaEnv.NewTable())
            {
                meta.Set("__index", luaEnv.Global);
                scriptScopeTable.SetMetaTable(meta);
                meta.Dispose();
            }
            
            // 将所需值注入到 Lua 脚本域中
            scriptScopeTable.Set("self", this);
            //注入mono gameobject和gameobject
            scriptScopeTable.Set("gameobject", gameObject);
            scriptScopeTable.Set("transform", transform);
            SetInjectValue();
            // 如果你希望在脚本内能够设置全局变量, 也可以直接将全局脚本域注入到当前脚本的脚本域中
            // 这样, 你就可以在 Lua 脚本中通过 Global.XXX 来访问全局变量
            // scriptScopeTable.Set("Global", luaEnv.Global);
            string luaScriptContent = XLuaManager.Instance.GetLuaScriptContent(luaScriptPath);
            // GlobalFunctions.LOG("luaScript.name={0}", luaScript.name);
            // 执行脚本
            luaEnv.DoString(luaScriptContent + registFunc, "LuaMyBehaviour", scriptScopeTable);
            
            // 从 Lua 脚本域中获取定义的函数
            Action luaAwake = scriptScopeTable.Get<Action>("Awake");
            scriptScopeTable.Get("Start", out luaStart);
            scriptScopeTable.Get("Update", out luaUpdate);
            scriptScopeTable.Get("OnDestroy", out luaOnDestroy);
            scriptScopeTable.Get("OnEnable", out luaOnEnable);
            scriptScopeTable.Get("OnDisable", out luaOnDisable);
            scriptScopeTable.Get("OnGUI", out luaOnGUI);
            scriptScopeTable.Get("LateUpdate", out luaLateUpdate);
            scriptScopeTable.Get("OnTriggerEnter", out luaOnTriggerEnter);
            if (luaAwake != null)
            {
                luaAwake();
            }
        }

        /// <summary>
        /// 碰撞相关
        /// </summary>
        /// <param name="other"></param>
        private void OnTriggerEnter(Collider other)
        {
            if (luaOnTriggerEnter != null)
            {
                luaOnTriggerEnter(other);
            }
        }

        void SetInjectValue()
        {
            if (scriptScopeTable == null)
            {
                return;
            }

            if (variables != null && variables.Variables != null)
            {
                foreach (Variable variable in variables.Variables)
                {
                    string name = variable.Name.Trim();
                    if (string.IsNullOrEmpty(name))
                        continue;
                    scriptScopeTable.Set(name, variable.GetValue());
                }
            }
        }

        private void LateUpdate()
        {
            if (luaLateUpdate != null)
            {
                luaLateUpdate();
            }
        }


        // Use this for initialization
        void Start()
        {
            if (luaStart != null)
            {
                luaStart();
            }
        }

        // Update is called once per frame
        void Update()
        {
            if (luaUpdate != null)
            {
                luaUpdate();
            }

            if (Time.time - lastGCTime > GCInterval)
            {
                if (luaEnv != null)
                {
                    luaEnv.Tick();
                }

                lastGCTime = Time.time;
            }
        }

        void OnGUI()
        {
            if (luaOnGUI != null)
            {
                luaOnGUI();
            }
        }


        void OnEnable()
        {
            if (luaOnEnable != null)
            {
                luaOnEnable();
            }
        }

        void OnDisable()
        {
            if (luaOnDisable != null)
            {
                luaOnDisable();
            }
        }

        void OnDestroy()
        {
            if (luaOnDestroy != null)
            {
                luaOnDestroy();
            }

            scriptScopeTable.Dispose();
            luaOnDestroy = null;
            luaOnEnable = null;
            luaOnDisable = null;
            luaOnGUI = null;
            luaUpdate = null;
            luaStart = null;
            luaLateUpdate = null;
            scriptScopeTable.Dispose();
            variables = null;
        }

        public object GetLuaObj()
        {
            return luaObj;
        }

        public void SetLuaObj(object obj)
        {
            luaObj = obj;
        }
    }
}